
package com.lry.rpc.starter.util;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.PropertyResolver;
import org.springframework.util.*;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

import static org.springframework.util.ClassUtils.getAllInterfacesForClass;
import static org.springframework.util.ClassUtils.resolveClassName;
import static org.springframework.util.StringUtils.hasText;

public abstract class AnnotationUtils {
    public static final String ANNOTATED_ELEMENT_UTILS_CLASS_NAME = "org.springframework.core.annotation.AnnotatedElementUtils";

    public AnnotationUtils() {
    }

    public static <A extends Annotation> boolean isPresent(Method method, Class<A> annotationClass) {
        Map<ElementType, List<A>> annotationsMap = findAnnotations(method, annotationClass);
        return !annotationsMap.isEmpty();
    }

    public static <A extends Annotation> Map<ElementType, List<A>> findAnnotations(Method method, Class<A> annotationClass) {
        Retention retention = (Retention)annotationClass.getAnnotation(Retention.class);
        RetentionPolicy retentionPolicy = retention.value();
        if (!RetentionPolicy.RUNTIME.equals(retentionPolicy)) {
            return Collections.emptyMap();
        } else {
            Map<ElementType, List<A>> annotationsMap = new LinkedHashMap();
            Target target = (Target)annotationClass.getAnnotation(Target.class);
            ElementType[] elementTypes = target.value();
            ElementType[] var7 = elementTypes;
            int var8 = elementTypes.length;

            for(int var9 = 0; var9 < var8; ++var9) {
                ElementType elementType = var7[var9];
                List<A> annotationsList = new LinkedList();
                switch(elementType) {
                case PARAMETER:
                    Annotation[][] parameterAnnotations = method.getParameterAnnotations();
                    Annotation[][] var21 = parameterAnnotations;
                    int var22 = parameterAnnotations.length;

                    for(int var23 = 0; var23 < var22; ++var23) {
                        Annotation[] annotations = var21[var23];
                        Annotation[] var17 = annotations;
                        int var18 = annotations.length;

                        for(int var19 = 0; var19 < var18; ++var19) {
                            Annotation annotation = var17[var19];
                            if (annotationClass.equals(annotation.annotationType())) {
                                annotationsList.add((A) annotation);
                            }
                        }
                    }
                    break;
                case METHOD:
                    A annotation = org.springframework.core.annotation.AnnotationUtils.findAnnotation(method, annotationClass);
                    if (annotation != null) {
                        annotationsList.add(annotation);
                    }
                    break;
                case TYPE:
                    Class<?> beanType = method.getDeclaringClass();
                    A annotation2 = org.springframework.core.annotation.AnnotationUtils.findAnnotation(beanType, annotationClass);
                    if (annotation2 != null) {
                        annotationsList.add(annotation2);
                    }
                }

                if (!annotationsList.isEmpty()) {
                    annotationsMap.put(elementType, annotationsList);
                }
            }

            return Collections.unmodifiableMap(annotationsMap);
        }
    }

    public static Map<String, Object> getAttributes(Annotation annotation, boolean ignoreDefaultValue, String... ignoreAttributeNames) {
        return getAttributes(annotation, (PropertyResolver)null, ignoreDefaultValue, ignoreAttributeNames);
    }

    public static Map<String, Object> getAttributes(Annotation annotation, PropertyResolver propertyResolver, boolean ignoreDefaultValue, String... ignoreAttributeNames) {
        Map<String, Object> annotationAttributes = org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes(annotation);
        String[] actualIgnoreAttributeNames = ignoreAttributeNames;

        if (ignoreDefaultValue && !CollectionUtils.isEmpty(annotationAttributes)) {
            List<String> attributeNamesToIgnore = new LinkedList(Arrays.asList(ignoreAttributeNames));

            annotationAttributes.forEach((attributeName,attributeValue)->{
                if (ObjectUtils.nullSafeEquals(attributeValue, org.springframework.core.annotation.AnnotationUtils.getDefaultValue(annotation, attributeName))) {
                    attributeNamesToIgnore.add(attributeName);
                }
            });
            actualIgnoreAttributeNames = attributeNamesToIgnore.toArray(new String[attributeNamesToIgnore.size()]);
        }

        return getAttributes(annotationAttributes, propertyResolver, actualIgnoreAttributeNames);
    }

    public static Map<String, Object> getAttributes(Map<String, Object> annotationAttributes, PropertyResolver propertyResolver, String... ignoreAttributeNames) {
        Set<String> ignoreAttributeNamesSet = new HashSet(CollectionUtils.arrayToList(ignoreAttributeNames));
        Map<String, Object> actualAttributes = new LinkedHashMap();
        Iterator var5 = annotationAttributes.entrySet().iterator();

        while(true) {
            String attributeName;
            Object attributeValue;
            do {
                if (!var5.hasNext()) {
                    return actualAttributes;
                }

                Entry<String, Object> annotationAttribute = (Entry)var5.next();
                attributeName = (String)annotationAttribute.getKey();
                attributeValue = annotationAttribute.getValue();
            } while(ignoreAttributeNamesSet.contains(attributeName));

            if (attributeValue instanceof String) {
                attributeValue = resolvePlaceholders(String.valueOf(attributeValue), propertyResolver);
            } else if (attributeValue instanceof String[]) {
                String[] values = (String[])((String[])attributeValue);

                for(int i = 0; i < values.length; ++i) {
                    values[i] = resolvePlaceholders(values[i], propertyResolver);
                }

                attributeValue = values;
            }

            actualAttributes.put(attributeName, attributeValue);
        }
    }

    public static String resolveInterfaceName(AnnotationAttributes attributes, Class<?> defaultInterfaceClass) {
        Boolean generic = getAttribute(attributes, "generic");
        if (generic != null && generic) {
            // it's a generic reference
            String interfaceClassName = getAttribute(attributes, "interfaceName");
            Assert.hasText(interfaceClassName,
                    "@Reference interfaceName() must be present when reference a generic service!");
            return interfaceClassName;
        }
        return resolveServiceInterfaceClass(attributes, defaultInterfaceClass).getName();
    }

    public static Class<?> resolveServiceInterfaceClass(AnnotationAttributes attributes, Class<?> defaultInterfaceClass)
            throws IllegalArgumentException {

        ClassLoader classLoader = defaultInterfaceClass != null ? defaultInterfaceClass.getClassLoader() : Thread.currentThread().getContextClassLoader();

        Class<?> interfaceClass = getAttribute(attributes, "interfaceClass");

        if (void.class.equals(interfaceClass)) { // default or set void.class for purpose.

            interfaceClass = null;

            String interfaceClassName = getAttribute(attributes, "interfaceName");

            if (hasText(interfaceClassName)) {
                if (ClassUtils.isPresent(interfaceClassName, classLoader)) {
                    interfaceClass = resolveClassName(interfaceClassName, classLoader);
                }
            }

        }

        if (interfaceClass == null && defaultInterfaceClass != null) {
            // Find all interfaces from the annotated class
            // To resolve an issue : https://github.com/apache/dubbo/issues/3251
            Class<?>[] allInterfaces = getAllInterfacesForClass(defaultInterfaceClass);

            if (allInterfaces.length > 0) {
                interfaceClass = allInterfaces[0];
            }

        }

        Assert.notNull(interfaceClass,
                "@Service interfaceClass() or interfaceName() or interface class must be present!");

        Assert.isTrue(interfaceClass.isInterface(),
                "The annotated type must be an interface!");

        return interfaceClass;
    }

    private static String resolvePlaceholders(String attributeValue, PropertyResolver propertyResolver) {
        String resolvedValue = attributeValue;
        if (propertyResolver != null) {
            resolvedValue = propertyResolver.resolvePlaceholders(attributeValue);
            resolvedValue = StringUtils.trimWhitespace(resolvedValue);
        }

        return resolvedValue;
    }

    public static <T> T getAttribute(Annotation annotation, String attributeName) {
        return getAttribute(org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes(annotation), attributeName);
    }

    public static <T> T getAttribute(Map<String, Object> attributes, String attributeName) {
        return getAttribute(attributes, attributeName, false);
    }

    public static <T> T getAttribute(Map<String, Object> attributes, String attributeName, boolean required) {
        T value = (T) getAttribute(attributes, attributeName, (Object)null);
        if (required && value == null) {
            throw new IllegalStateException("The attribute['" + attributeName + "] is required!");
        } else {
            return value;
        }
    }

    public static <T> T getAttribute(Map<String, Object> attributes, String attributeName, T defaultValue) {
        T value = (T) attributes.get(attributeName);
        return value == null ? defaultValue : value;
    }

    public static <T> T getRequiredAttribute(Map<String, Object> attributes, String attributeName) {
        return getAttribute(attributes, attributeName, true);
    }

    public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, boolean ignoreDefaultValue, String... ignoreAttributeNames) {
        return getAnnotationAttributes(annotation, (PropertyResolver)null, ignoreDefaultValue, ignoreAttributeNames);
    }

    public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, PropertyResolver propertyResolver, boolean ignoreDefaultValue, String... ignoreAttributeNames) {
        return AnnotationAttributes.fromMap(getAttributes(annotation, propertyResolver, ignoreDefaultValue, ignoreAttributeNames));
    }

    public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement annotatedElement, Class<? extends Annotation> annotationType, PropertyResolver propertyResolver, boolean ignoreDefaultValue, String... ignoreAttributeNames) {
        Annotation annotation = annotatedElement.getAnnotation(annotationType);
        return annotation == null ? null : getAnnotationAttributes(annotation, propertyResolver, ignoreDefaultValue, ignoreAttributeNames);
    }

    public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement annotatedElement, Class<? extends Annotation> annotationType,
                                                               PropertyResolver propertyResolver, boolean ignoreDefaultValue,
                                                               boolean tryMergedAnnotation, String... ignoreAttributeNames) {
        AnnotationAttributes attributes = null;
        if (tryMergedAnnotation) {
            attributes = tryGetMergedAnnotationAttributes(annotatedElement, annotationType, propertyResolver, ignoreDefaultValue, ignoreAttributeNames);
        }

        if (attributes == null) {
            attributes = getAnnotationAttributes(annotatedElement, annotationType, propertyResolver, ignoreDefaultValue, ignoreAttributeNames);
        }

        return attributes;
    }

    public static Annotation tryGetMergedAnnotation(AnnotatedElement annotatedElement, Class<? extends Annotation> annotationType) {
        Annotation mergedAnnotation = null;
        ClassLoader classLoader = annotationType.getClassLoader();
        if (ClassUtils.isPresent("org.springframework.core.annotation.AnnotatedElementUtils", classLoader)) {
            Class<?> annotatedElementUtilsClass = ClassUtils.resolveClassName("org.springframework.core.annotation.AnnotatedElementUtils", classLoader);
            Method getMergedAnnotationMethod = ReflectionUtils.findMethod(annotatedElementUtilsClass, "getMergedAnnotation", new Class[]{AnnotatedElement.class, Class.class});
            if (getMergedAnnotationMethod != null) {
                mergedAnnotation = (Annotation)ReflectionUtils.invokeMethod(getMergedAnnotationMethod, (Object)null, new Object[]{annotatedElement, annotationType});
            }
        }

        return mergedAnnotation;
    }

    public static AnnotationAttributes tryGetMergedAnnotationAttributes(AnnotatedElement annotatedElement, Class<? extends Annotation> annotationType, PropertyResolver propertyResolver, boolean ignoreDefaultValue, String... ignoreAttributeNames) {
        Annotation annotation = tryGetMergedAnnotation(annotatedElement, annotationType);
        return annotation == null ? null : getAnnotationAttributes(annotation, propertyResolver, ignoreDefaultValue, ignoreAttributeNames);
    }
}
